package com.we.wfc.common.utils;


import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.we.wfc.common.annotation.CopyPropertiesStrategy;
import com.we.wfc.common.annotation.NullStrategy;
import com.we.wfc.common.base.BaseException;
import com.we.wfc.common.enums.ReturnCode;
import com.we.wfc.common.serialize.IgnoreStrategy;
import com.we.wfc.common.serialize.OffsetDateTimeDeserializer;
import com.we.wfc.common.serialize.OffsetDateTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 转换器工具类
 * @Author:Liangzy(Feeling)
 * @Date:Create in 2019/10/31 6:03 下午
 */
@Slf4j
public final class ConverterUtil {

    /**
     * 时间格式：时间戳24小时制型(yyyy/MM/dd HH:mm:ss.SSS)
     */
    public static final String FORMATE_TIME_STAMP_24H = "yyyy/MM/dd HH:mm:ss.SSS";

    /**
     * 时间格式：时间戳24小时制型(yyyy-MM-dd HH:mm:ss.SSS)
     */
    public static final String FORMATE_TIME_STAMP_24H_MLINE = "yyyy-MM-dd HH:mm:ss.SSS";

    /**
     * 时间格式：日期时间24小时制型(yyyy/MM/dd HH:mm:ss)
     */
    public static final String FORMATE_DATE_TIME_24H = "yyyy/MM/dd HH:mm:ss";

    /**
     * 随机类型:0 数字
     */
    public static final String RANDOM_TYPE_NUM = "0";
    /**
     * 随机类型:1 英文
     */
    public static final String RANDOM_TYPE_TEXT = "1";

    /**
     * 随机类型:2 数字和英文混合
     */
    public static final String RANDOM_TYPE_NUM_AND_TEXT = "2";

    /**
     * 随机
     */
    private static Random randGen = new SecureRandom();

    /**
     * 字符串随机因子:数字
     */
    private static String numbers = "0123456789";

    /**
     * 字符串随机因子: 英文
     */
    private static String letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    /**
     * 字符串随机因子:英数混合
     */
    private static char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();

    /**
     * Gson
     */
    private static IgnoreStrategy gsonIgnoreStrategy = new IgnoreStrategy();
    private static OffsetDateTimeSerializer gsonOffSer = new OffsetDateTimeSerializer();
    private static OffsetDateTimeDeserializer gsonOffDes = new OffsetDateTimeDeserializer();

    /**
     * Object->String转换
     * <p>
     * 入参是null时，返回值为nullValue
     *
     * @param obj       Object
     * @param nullValue 空值默认值
     * @return String
     */
    public static String toString(Object obj, String nullValue) {
        if (isEmpty(obj)) {
            return nullValue;
        }
        if (obj instanceof String) {
            return obj.toString();
        }
        if (obj instanceof Integer) {
            return Integer.toString(toInteger(obj));
        }
        if (obj instanceof Double) {
            return toString(toBigDecimal(toDouble(obj)));
        }
        if (obj instanceof Long) {
            return Long.toString(toLong(obj));
        }
        if (obj instanceof BigDecimal) {
            return toBigDecimal(obj).toPlainString();
        }
        if (obj instanceof Timestamp) {
            return dateToString((Date) obj, FORMATE_TIME_STAMP_24H);
        }
        if (obj instanceof Date) {
            return dateToString((Date) obj, FORMATE_DATE_TIME_24H);
        }
        if (obj instanceof Object[]) {
            Object[] objArray = (Object[]) obj;
            return String.valueOf(objArray[0]);
        }
        return String.valueOf(obj);
    }

    /**
     * 判断是否是空字符串 null和"" 都返回 true
     *
     * @param
     * @return
     * @author Robin Chang
     */
    public static boolean isEmpty(Object str) {
        boolean flag = true;
        if (str != null && !str.equals("")) {
            if (str instanceof Object[]) {
                return ((Object[]) str).length <= 0;
            }
            if (str instanceof Collection<?>) {
                return ((Collection<?>) str).size() <= 0;
            }
            if (str instanceof Map) {
                return ((Map<?, ?>) str).size() <= 0;
            }
            String newStr = str.toString();
            if (newStr == null || newStr == "" || "[]".equals(newStr) || "null".equals(newStr)) {
                flag = true;
            } else if (newStr.length() > 0) {
                flag = false;
            }
        } else {
            flag = true;
        }
        return flag;
    }

    /**
     * Object->Integer转换
     * <p>
     * 入参是null时，返回值为null
     *
     * @param obj Object
     * @return Integer数值
     */
    public static Integer toInteger(Object obj) {
        return toInteger(obj, null);
    }

    /**
     * Object->Integer转换
     * <p>
     * 入参是null时，返回值为nullValue
     *
     * @param obj Object
     * @return Integer数值
     */
    public static Integer toInteger(Object obj, Integer nullValue) {
        if (isEmpty(obj)) {
            return nullValue;
        }
        if (obj instanceof Double) {
            return ((Double) obj).intValue();
        }
        return Integer.valueOf(obj.toString());
    }

    /**
     * Object->String转换
     * <p>
     * 入参是null时，返回值为null
     *
     * @param obj Object
     * @return String
     */
    public static String toString(Object obj) {
        return toString(obj, null);
    }

    /**
     * Object->BigDecimal转换
     * <p>
     * 入参是null时，返回值为null
     *
     * @param obj Object
     * @return BigDecimal数值
     */
    public static BigDecimal toBigDecimal(Object obj) {
        return toBigDecimal(obj, null);
    }

    /**
     * Object->BigDecimal转换
     * <p>
     * 入参是null时，返回值为nullValue
     *
     * @param obj Object
     * @return BigDecimal数值
     */
    public static BigDecimal toBigDecimal(Object obj, BigDecimal nullValue) {
        if (isEmpty(obj)) {
            return nullValue;
        }
        return new BigDecimal(obj.toString());
    }

    /**
     * Object->Long转换
     * <p>
     * 入参是null时，返回值为null
     *
     * @param obj Object
     * @return Long型数值
     */
    public static Long toLong(Object obj) {
        return toLong(obj, null);
    }

    /**
     * Object->Long转换
     * <p>
     * 入参是null时，返回值为nullValue
     *
     * @param obj Object
     * @return Long型数值
     */
    public static Long toLong(Object obj, Long nullValue) {
        if (isEmpty(obj)) {
            return nullValue;
        }
        if (obj instanceof Long) {
            return Long.valueOf(obj.toString());
        }
        if (obj instanceof Double) {
            return toDouble(obj).longValue();
        }
        if (obj instanceof BigDecimal) {
            return toBigDecimal(obj).longValue();
        }
        if (obj instanceof Date) {
            return toDate(obj, null).getTime();
        }
        return new Long(obj.toString());
    }

    /**
     * Object->Double转换
     * <p>
     * 入参是null时，返回值为null
     *
     * @param obj Object
     * @return Double型数值
     */
    public static Double toDouble(Object obj) {
        return toDouble(obj, null);
    }

    /**
     * Object->Double转换
     * <p>
     * 入参是null时，返回值为nullValue
     *
     * @param obj Object
     * @return Double型数值
     */
    public static Double toDouble(Object obj, Double nullValue) {
        if (isEmpty(obj)) {
            return nullValue;
        }
        return new Double(obj.toString());
    }

    /**
     * Object->Date转换
     * <p>
     * 入参是null时，返回值为null
     *
     * @param obj date
     * @return Date数值
     */
    public static Date toDate(Object obj) {
        return toDate(obj, null);
    }

    /**
     * Object->Date转换
     * <p>
     * 入参是null时，返回值为null
     *
     * @param obj Object
     * @return Date数值
     */
    public static Date toDate(Object obj, String formate) {
        if ((obj == null) || ("".equals(obj))) {
            return null;
        }
        Date dateTemp = null;
        if (obj instanceof Long) {
            Calendar cl = Calendar.getInstance();
            cl.setTimeInMillis(toLong(obj));
            dateTemp = cl.getTime();
        }
        if (obj instanceof Timestamp) {
            dateTemp = new Date(((Timestamp) obj).getTime());
        }
        if (obj instanceof Timestamp) {
            dateTemp = new Date(((Timestamp) obj).getTime());
        }
        if (obj instanceof Date) {
            dateTemp = (Date) obj;
        }
        if (obj instanceof java.sql.Date) {
            dateTemp = new Date(((java.sql.Date) obj).getTime());
        }
        if (obj instanceof String) {
            String temp = obj.toString();
            if (temp.length() == 8) {
                char[] arra = temp.toCharArray();
                String temp1 = new String(arra, 0, 4);
                String temp2 = new String(arra, 4, 2);
                String temp3 = new String(arra, 6, 2);
                obj = temp1 + "/" + temp2 + "/" + temp3;
            }
            if (isEmpty(formate)) {
                return dateTemp;
            }
            DateFormat df = new SimpleDateFormat(formate);
            try {
                dateTemp = df.parse(obj.toString());
            } catch (ParseException e) {
                throw new RuntimeException("日期不合法", e);
            }
            return new Date(dateTemp.getTime());
        }
        return dateTemp;
    }

    /**
     * 日期->字符串转换
     *
     * @param date   日期
     * @param format 转换格式
     * @return 字符串
     */
    public static String dateToString(Date date, String format) {
        SimpleDateFormat sf = new SimpleDateFormat(format);
        return sf.format(date);
    }

    /**
     * 取得N位随机验证码
     * <p/>
     * 1-配置长度，随机内容
     *
     * @return
     */
    public static String getCheckCode(String type, Integer length) {
        // 默认6位
        int nMax = 6;
        if (null != length) {
            nMax = length;
        }

        // 随机串数字
        String str = numbers;
        if (RANDOM_TYPE_TEXT.equals(type)) {
            // 英文
            str = letters;
        } else if (RANDOM_TYPE_NUM_AND_TEXT.equals(type)) {
            // 英数混合
            return randomMix(nMax);
        }

        Random contRandom = new Random();
        StringBuffer sb = new StringBuffer();
        // 产生随机数
        for (int i = 0; i < nMax; i++) {
            int number = contRandom.nextInt(str.length());
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 随机生成英数混合
     *
     * @param length 长度
     * @return
     */
    public static String randomMix(int length) {
        if (length < 1) {
            return null;
        }
        // Create a char buffer to put random letters and numbers in.
        char[] randBuffer = new char[length];
        for (int i = 0; i < randBuffer.length; i++) {
            randBuffer[i] = numbersAndLetters[randGen.nextInt(71)];
        }
        return new String(randBuffer);
    }

    /**
     * 日期操作(加减)
     *
     * @param date         日期
     * @param calendarType 日历类型
     * @param num          变化值
     * @return
     */
    public static Date addDate(Date date, int calendarType, int num) {
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTime(date);
        gc.add(calendarType, num);
        return gc.getTime();
    }

    /**
     * 判断对象是否不为空
     *
     * @param str
     * @return
     */
    public static boolean isNotEmpty(Object str) {
        return !isEmpty(str);
    }

    /**
     * 判断对象数组是否都不为空
     *
     * @return
     */
    public static boolean isNotEmpty(Object... strs) {
        if (strs == null || 0 == strs.length) {
            return false;
        }
        boolean result = true;
        for (Object obj : strs) {
            result = (result && isNotEmpty(obj));
            if (!result) {
                return result;
            }
        }
        return result;
    }

    /**
     * 根据错误码比对返回相应的msg
     */
    public static Object loadException2ResultPoJo(BaseException e) {

        //获取枚举的list
        List<Map<String, Object>> list = ReturnCode.toList();
        Object msg = list.stream().map(x -> x.get(e.getErrorCode())).findFirst().get();
        if (isNotEmpty(msg)) {
            return msg;
        }
        return "未找到错误信息";
    }

    /**
     * 解析内容获得验证码
     */
    public static String analyzeText(String text) {
        String[] split = text.split(":");
        return split[1];
    }

    /**
     * 获取某类中所有的Field属性
     *
     * @param clazz 类
     * @return
     */
    public static List<Field> getAllFieldsByAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClazz) {
        Field[] fileds = getAllFields(clazz);
        List<Field> filedList = Lists.newArrayList();
        for (Field field : fileds) {
            if (field.isAnnotationPresent(annotationClazz)) {
                filedList.add(field);
            }
        }
        return filedList;
    }

    /**
     * 获取某类中所有的Field属性
     *
     * @param clazz 类
     * @return
     */
    public static Field[] getAllFields(Class<?> clazz) {
        Field[] fileds = {};
        fileds = ArrayUtils.addAll(clazz.getDeclaredFields());
        Class<?> parent = clazz.getSuperclass();
        if (null != parent) {
            fileds = ArrayUtils.addAll(fileds, getAllFields(parent));
        }
        if (fileds.length > 0) {
            List<Field> tempList = Lists.newArrayList();
            for (Field field : fileds) {
                // 是否为复合字段 如果使用了jacoco会多出一些$jacocoData的属性，用这个可以过滤
                if (field.isSynthetic()) {
                    continue;
                }
                tempList.add(field);
            }
            return tempList.stream().toArray(Field[]::new);
        }
        return fileds;
    }

    /**
     * 拷贝属性
     *
     * @param from     来源Object
     * @param target   目标Object
     * @param excludes 排除属性
     */
    public static void copyProperties(Object from, Object target, String... excludes) {
        Map<String, String> excludeMap = Maps.newHashMap();
        // 如果排除的属性不为空则转成Map
        if (null != excludes) {
            excludeMap = Arrays.asList(excludes).stream().collect(Collectors.toMap(String::toString, String::toString));
        }
        // 获取(属性名,属性值)的map
        Map<String, Field> fromFieldsMap = getAllFieldsNameMap(from);
        // 获取target全部属性
        Field[] targetFields = getAllFields(target.getClass());
        // 遍历
        for (Field tField : targetFields) {
            String fName = tField.getName();
            // 如果某属性在 from中包含 且 在排除中不包含 则进行set操作
            if (fromFieldsMap.containsKey(fName) && !excludeMap.containsKey(fName)) {
                try {
                    tField.setAccessible(true);
                    Field fField = fromFieldsMap.get(fName);
                    Object val = fField.get(from);
                    // 如果是空值，则使用空值策略注解
                    if (ConverterUtil.isEmpty(val)) {
                        // 先从target读
                        NullStrategy strategy = tField.getAnnotation(NullStrategy.class);
                        // 如果为空的话再从from读
                        if (ConverterUtil.isEmpty(strategy)) {
                            strategy = fField.getAnnotation(NullStrategy.class);
                        }
                        // 还是不为空才能进行判断
                        if (ConverterUtil.isNotEmpty(strategy)) {
                            // 如果空值跳过，则不进行set操作
                            if (strategy.except()) {
                                continue;
                            }
                            // 如果需要使用转换器
                            if (strategy.useExchager()) {
                                val = strategy.exchager().newInstance().exchage(strategy.value());
                            } else {
                                val = strategy.value();
                            }
                        }
                    } else {
                        // val如果不是空值时，如果也配置了值转换器，就先转换一下
                        CopyPropertiesStrategy strategy = tField.getAnnotation(CopyPropertiesStrategy.class);
                        // 如果为空的话再从from读
                        if (ConverterUtil.isEmpty(strategy)) {
                            strategy = fField.getAnnotation(CopyPropertiesStrategy.class);
                        }
                        if (ConverterUtil.isNotEmpty(strategy)) {
                            // 获取属性的值
                            Object oldVal = tField.get(target);
                            // 经过转换器转换的值
                            val = strategy.exchager().newInstance().exchage(oldVal, val);
                        }
                    }
                    tField.set(target, val);
                } catch (Exception e) {
                    // 如果遇到问题就跳过
                    continue;
                }
            }
        }
    }

    /**
     * 获取某类中所有的Field名称和Field的关系map
     *
     * @param entity 类
     * @return
     */
    public static Map<String, Field> getAllFieldsNameMap(Object entity) {
        Map<String, Field> map = new HashMap<String, Field>();
        try {
            for (Field field : getAllFields(entity.getClass())) {
                field.setAccessible(true);
                map.put(field.getName(), field);
            }
        } catch (Exception e) {
            // 修正 Use a logger to log this exception.
            log.error("exception in getAllFieldsNameMap", e);
        }
        return map;
    }

    public static final Gson gson = new GsonBuilder().addSerializationExclusionStrategy(gsonIgnoreStrategy).addDeserializationExclusionStrategy(gsonIgnoreStrategy)
            .registerTypeAdapter(Long.class, new TypeAdapter<Long>() {
                @Override
                public void write(JsonWriter out, Long value) throws IOException {
                    out.value(ConverterUtil.toString(value));
                }

                @Override
                public Long read(JsonReader in) throws IOException {
                    return ConverterUtil.toLong(in.nextString());
                }
            }).registerTypeAdapter(OffsetDateTime.class, new TypeAdapter<OffsetDateTime>() {
                @Override
                public void write(JsonWriter out, OffsetDateTime value) throws IOException {
                    out.value(gsonOffSer.getSerializeStr(value));
                }

                @Override
                public OffsetDateTime read(JsonReader in) throws IOException {
                    return gsonOffDes.getDeserialized(in.nextString());
                }
            }).create();

    /**
     * Object转换成绝对值
     * <p/>
     * Math.abs有越界的问题，一旦输入数为边界值就无法得到正确的结果
     *
     * @param obj
     * @return
     */
    public static String abs(Object obj) {
        String temp = toString(obj, "");
        if (temp.startsWith("-")) {
            temp = temp.substring(1);
        }
        return temp;
    }

    /**
     * 获取补零字符串
     *
     * @param num
     *            要补零的数字
     * @param length
     *            补零后的长度
     * @return
     */
    public static String getZeroFill(int num, int length) {
        String str = String.format("%0" + length + "d", num);
        return str;
    }

}
